package dabs.engine.plant;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import dabs.engine.Updatable;
import dabs.engine.messaging.Notification;
import dabs.engine.messaging.NotificationHandler;
import dabs.engine.control.ComponentCommand;
import dabs.engine.environment.AcceptsModifier;
import dabs.engine.environment.Modifier;
import dabs.engine.environment.TemporaryDisable;

public abstract class ComponentPart implements Updatable, AcceptsModifier, Serializable{
	private static final long serialVersionUID = -1899779992539447069L;

	private HydraulicState oldHydroState;
	private HydraulicState newHydroState;
	private HydraulicProperty hydroProp;
	private int health;
	private String name;
	private NotificationHandler mailBox;
	private boolean deactivatedFlag;
	private LinkedList<Modifier> activeModifiers;
	private ArrayList<ComponentPart> outputs = new ArrayList<ComponentPart>();
	
	public ComponentPart(NotificationHandler mailBox, String name) {
		this.name = name;
		this.mailBox = mailBox;
		health = 100;
		activeModifiers = new LinkedList<Modifier>();
		oldHydroState = new HydraulicState();
		newHydroState = new HydraulicState();
	}
	
	public void setOutputs(ComponentPart output) {
		outputs.add(output);
	}
	
	public HydraulicState getOldHydroState() { return oldHydroState; }
	public HydraulicState getNewHydroState() { return newHydroState; }
	public HydraulicProperty getHydroProp() { return hydroProp; }
	
	public int getHealth() { return health; }
	
	// note the protected - package only visibility
	protected void setHealth(int health) { 
		if (this.health != 0 && health == 0) // we just died
			justDied();
		this.health = 0;
	}
	
	public String getName() { return name; }
	public ArrayList<ComponentPart> getOutputs() { return outputs; }
	
	protected boolean isDeactivated() { return deactivatedFlag; }
	protected void setDeactivated() { deactivatedFlag = true; } // overloaded methods
	protected void setDeactivated(boolean value) {deactivatedFlag = value; }
	
	public boolean isDead() { return health == 0; }
	
	// overloading functions
	protected void passMessage(String from, String contents) {
		passMessage(new Notification(from, contents));
	}
	
	protected void passMessage(Notification notif){
		mailBox.handleNotification(notif);
	}
	
	// subclasses can reimplement
	protected void justDied() {
		passMessage(getName(), "Component has just been destroyed!");
	}

	public void handleModifier(Modifier modifier) {
		if (isDead()) {
			return; // silently return if we're dead
		} else {
			receiveModifier(modifier);
			activeModifiers.add(modifier);
		}
	}
	
	protected void receiveModifier(Modifier modifier) {
		if (modifier instanceof TemporaryDisable) {
			passMessage(new Notification(getName(), "Stalling"));
			setDeactivated();
		} else { 
			throw new RuntimeException("wtf is this modifier? " + modifier.getClass().getName());
		}
	};
	
	protected void inactivatedModifier(Modifier modifier) {
		if (modifier instanceof TemporaryDisable)
			setDeactivated(false);
	};
	

	// each component should reimplement it
	public void runCommand(ComponentCommand command) {
		if (isDead()) {
			passMessage(getName(), "Component has been destroyed");
			return;
		}
	}
	
	public void tick() {
		if (isDead()) return; // do nothing
		
		Iterator<Modifier> it = activeModifiers.iterator();
		while (it.hasNext()) {
			Modifier m = it.next();
			m.decrement();
			if (m.getDuration() == 0) {
				inactivatedModifier(m);
				it.remove();
			}
		}
		oldHydroState = newHydroState;
		if (health < 10){
			mailBox.handleNotification(new Notification(getName(), "Component is low on health!"));
		}
	}
	
	public void print() {
		System.out.print(getName());
		System.out.println(oldHydroState.toString());
	}
	
	public String read() {
		return oldHydroState.toString();
	}
	
	public int getAttribute(String attribute) throws Exception {
		if (attribute.equals("steam"))
			return oldHydroState.getSteamLevel();
		else if (attribute.equals("water"))
			return oldHydroState.getWaterLevel();
		else
			throw new Exception("Invalid Attribute");
	}
}
